#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string>
#include <string.h>
#define THREAD_NUM 4
typedef void (*func_t)(const std::string &name,pthread_mutex_t* pmtx,pthread_cond_t* pcon);
volatile bool quit = false;
class ThreadData
{
public:
    ThreadData(const std::string &name, func_t func,pthread_mutex_t* pmtx,pthread_cond_t* pcon)
        : name_(name), func_(func), pmtx_(pmtx), pcon_(pcon)
    {
    }

public:
    const std::string name_;
    func_t func_;
    pthread_mutex_t* pmtx_;
    pthread_cond_t* pcon_;
};

void func1(const std::string &name,pthread_mutex_t* pmtx,pthread_cond_t* pcon)
{
    while (!quit)
    {
        // wait一定要在加锁和解锁之间wait，因为我么是要在临界资源没就位时等，而检查临界资源是否就位
        // 之前要先访问临界资源，只有拥有锁才可以访问临界资源，所以wait要在加锁和解锁之间
        pthread_mutex_lock(pmtx);
        // if(临界资源是否就绪)
        pthread_cond_wait(pcon,pmtx);// 默认在执行该代码时，wait代码在执行，当前进程会被立即阻塞
        // while(1);
        std::cout << name << "running -a" << std::endl;
        pthread_mutex_unlock(pmtx);
        // std::cout.flush();
        // sleep(1);
    }
}
void func2(const std::string &name,pthread_mutex_t* pmtx,pthread_cond_t* pcon)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcon,pmtx);
        std::cout << name << "running -b" << std::endl;
        pthread_mutex_unlock(pmtx);
        // std::cout.flush();

        // sleep(1);
    }
}
void func3(const std::string &name,pthread_mutex_t* pmtx,pthread_cond_t* pcon)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcon,pmtx);
        std::cout << name << "running -c" << std::endl;
        pthread_mutex_unlock(pmtx);
        // std::cout.flush();

        // sleep(1);
    }
}
void func4(const std::string &name,pthread_mutex_t* pmtx,pthread_cond_t* pcon)
{
    while (!quit)
    {
        pthread_mutex_lock(pmtx);
        pthread_cond_wait(pcon,pmtx);
        std::cout << name << "running -d" << std::endl;
        pthread_mutex_unlock(pmtx);
        // std::cout.flush();

        // sleep(1);
    }
}
void *Entry(void *args)
{
    ThreadData *td = (ThreadData *)args;
    td->func_(td->name_,td->pmtx_,td->pcon_);
    delete td;
    return nullptr;
}

int main()
{
    pthread_mutex_t mtx;
    pthread_cond_t con;
    pthread_mutex_init(&mtx,nullptr);
    pthread_cond_init(&con,nullptr);
    pthread_t tids[THREAD_NUM];
    func_t func[THREAD_NUM] = {func1, func2, func3, func4};
    for (int i = 0; i < THREAD_NUM; i++)
    {
        std::string name("thread ");
        name += std::to_string(i + 1);

        ThreadData *td = new ThreadData(name, func[i],&mtx,&con);
        pthread_create(tids + i, nullptr, Entry, (void *)td);
    }
    sleep(1);
    int cnt = 5;
    while(cnt--)
    {
        std::cout << "thread running code cnt:" << cnt << std::endl;
        pthread_cond_signal(&con);
        //pthread_cond_broadcast(&con);
        sleep(1);
    }
    
    std::cout << "control over" << std::endl;
    quit = true;
    pthread_cond_broadcast(&con);

    for (int i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(tids[i], nullptr);
        std::cout << "thread：" << tids[i] << "quit" << std::endl;
    }

    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&con);
    return 0;
}